package com.projectframe.base.db.dao;

import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

@Repository("baseDao")
public class BaseDaoSearch extends HibernateDaoSupport
{
    protected final Logger log = Logger.getLogger(this.getClass());
    
    private static final int HQL_STATE = 1;
    private static final int SQL_STATE = 2;
    
    @SuppressWarnings("unchecked")
    private <T> T findBySQL(final String sql, final Map<?, ?> argsMap,
                            final int startRow, final int pageRows, 
                            final Class<?> entityClass, final Class<?> tfClass)
    {
        return (T)getHibernateTemplate().executeFind(new HibernateCallback<List<?>>()
        {
            public List<?> doInHibernate(Session session)
            {
                Query query = null;
                
                if(entityClass!=null)
                    query = session.createSQLQuery(sql).addEntity(entityClass);
                else if(tfClass!=null)
                    query = session.createSQLQuery(sql).setResultTransformer(new BaseDaoColumnToBean(tfClass));
                else
                    query = session.createSQLQuery(sql);

                if (argsMap != null)
                    query.setProperties(argsMap);

                query.setFirstResult(startRow);

                if (pageRows!=0)
                    query.setMaxResults(pageRows);

                return query.list();
            }
        });
    }
    
    /**
     * 根據SQL條件與查詢條件QueryObject取得資料，前端分頁顯示用
     * 備註：queryObject只負責記錄分頁資訊，參數值都由argsMap取得
     * 
     * @param sql
     * @param argsMap
     * @param queryObj
     * @return
     */
    public <T> T findBySQLWithQueryObject(final String sql, final Map<?, ?> argsMap,
                                          final int startRow, final int pageRows)
    {
        return findBySQL(sql, argsMap, startRow, pageRows, null, null);
    }
    
    /**
     * 根據SQL條件與查詢條件QueryObject取得資料，前端分頁顯示用
     * 備註：queryObject只負責記錄分頁資訊，參數值都由argsMap取得
     * 
     * @param sql
     * @param argsMap
     * @param queryObj
     * @param clazz
     *            轉換的物件類型
     * @return
     */
    public <T> T findBySQLWithQueryObject(final String sql, final Map<?, ?> argsMap,
                                          final int startRow, final int pageRows, final Class<?> clazz)
    {
        return findBySQL(sql, argsMap, startRow, pageRows, null, clazz);
    }

    /**
     * 根據SQL條件與查詢條件, 取得資料 註:只能接受select * from xxx 的方式.
     * 
     * @param sql
     * @param argsMap
     * @param queryObj
     * @param clazz
     *            轉換的物件類型
     * @return
     */
    public <T> T findBySQLWithQueryEntity(final String sql, final Map<?, ?> argsMap,
                                          final int startRow, final int pageRows, final Class<?> clazz)
    {
        return findBySQL(sql, argsMap, startRow, pageRows, clazz, null);
    }

    /**
     * 根據HQL條件與查詢條件QueryObject取得資料，前端分頁顯示用
     * 備註：queryObject只負責記錄分頁資訊，參數值都由argsMap取得
     * 
     * @param hql
     * @param argsMap
     * @param queryObj
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T findByHQLWithQueryObject(final String hql, final Map<?, ?> argsMap,
                                          final int startRow, final int pageRows)
    {
        return (T)getHibernateTemplate().executeFind(new HibernateCallback<List<?>>()
        {
            public List<?> doInHibernate(Session session)
            {
                Query query = session.createQuery(hql);

                if (argsMap != null)
                {
                    query.setProperties(argsMap);
                }

                query.setFirstResult(startRow);
                if (pageRows != 0)
                    query.setMaxResults(pageRows);

                return query.list();
            }
        });
    }

    public Long getCountByHQL(final String hql, final Map<?, ?> argsMap)
    {
        final int index = hql.toUpperCase().indexOf("FROM");
        final int orderByIdx = hql.toUpperCase().indexOf("ORDER BY");
        String fromhql = "";

        if(orderByIdx==-1)
            fromhql = hql.substring(index);
        else
            fromhql = hql.substring(index, orderByIdx - 1);

        final String newHql = "select count(*) " + fromhql;
        return getCount(newHql, argsMap, HQL_STATE);
    }
    
    public Long getCountBySQL(final String sql, final Map<?, ?> argsMap)
    {
        final String newSql = "select count(*) from (" + sql + ") as countTable";
        return getCount(newSql, argsMap, SQL_STATE);
    }
    
    private Long getCount(final String str, final Map<?, ?> argsMap, final int flag)
    {
        List<?> list = (List<?>) getHibernateTemplate().executeFind(new HibernateCallback<List<?>>()
        {
            public List<?> doInHibernate(Session session)
            {
                Query query = null;
                
                if(flag==HQL_STATE)
                    query = session.createQuery(str);
                else if(flag==SQL_STATE)
                    query = session.createSQLQuery(str);
                else
                    return null;

                if (argsMap != null)
                    query.setProperties(argsMap);

                return query.list();
            }
        });
        
        return ((Number) list.get(0)).longValue();
    }

    // ==============================================================================================
    // ==============================================================================================
    // ==============================================================================================
    // ==============================================================================================
    
//    public <T> T findByPk(final Class<?> refClass, final Serializable key)
//    {
//        return load(refClass, key);
//    }
//
//    @SuppressWarnings("unchecked")
//    public <T> T load(final Class<?> refClass, final Serializable key)
//    {
//        // Leon: 不直接用 HibernateTemplate.load, 因為它會拋出 ObjectNotFoundException 給外面
//        // 改成如果真的 ObjectNotFoundException, 就回傳 null
//        return (T)this.getHibernateTemplate().execute(new HibernateCallback<Object>()
//        {
//            public Object doInHibernate(final Session session)
//            {
//                try
//                {
//                    return session.get(refClass, key);
//                }
//                catch (ObjectNotFoundException e)
//                {
//                    return null;
//                }
//            }
//        });
//    }
//
//    @SuppressWarnings("unchecked")
//    public <T> T findByExample(final Object model)
//    {
//        return (T)this.getHibernateTemplate().findByExample(model);
//    }

    //======================================================================
    //Insert
    //======================================================================

//    public <T> T insertData(final T model, final boolean flushFlag)
//    {
//        this.getHibernateTemplate().save(model);
//        
//        if (flushFlag)
//            flushData();
//        
//        return model;
//    }
//
//    public <T> T insertData(final T model)
//    {
//        return insertData(model, false);
//    }
    
    //======================================================================
    //Update
    //======================================================================
    
//    public <T> T updateData(final T model, final boolean flushFlag)
//    {
//        this.getHibernateTemplate().update(model);
//        
//        if(flushFlag)
//            flushData();
//        
//        return model;
//    }
//
//    public <T> T updateData(final T model)
//    {
//        return updateData(model, false);
//    }
    
    //======================================================================
    //Delete
    //======================================================================
    
//    public void deleteData(Object model, final boolean flushFlag)
//    {
//        this.getHibernateTemplate().delete(model);
//        
//        if(flushFlag)
//            flushData();
//    }
//
//    public void deleteData(Object model)
//    {
//        deleteData(model, false);
//    }
    
    //======================================================================
    //Flush
    //====================================================================== 

//    public void flushData()
//    {
//        this.getHibernateTemplate().flush();
//    }
    
    @Autowired
    public void registerSessionFactory(SessionFactory sessionFactory)
    {
        super.setSessionFactory(sessionFactory);
        getHibernateTemplate().setFlushMode(HibernateTemplate.FLUSH_EAGER);
    }
}
